﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using DenisVuyka.Controls.PropertyGrid.Metadata;

namespace DenisVuyka.Controls.PropertyGrid.Data
{
  /// <summary>
  /// Represents a collection of property items
  /// </summary>
  public class PropertyItemCollection
  {
    #region Properties

    public PropertyGrid Owner { get; protected set; }
    public object Component { get; protected set; }

    private readonly ObservableCollection<PropertyItem> _items = new ObservableCollection<PropertyItem>();
    /// <summary>
    /// Gets the items.
    /// </summary>
    /// <value>The items.</value>
    public ObservableCollection<PropertyItem> Items
    {
      get { return _items; }
    }

    #endregion

    #region ctor

    /// <summary>
    /// Initializes a new instance of the <see cref="PropertyItemCollection"/> class.
    /// </summary>
    public PropertyItemCollection() : base() { }

    /// <summary>
    /// Initializes a new instance of the <see cref="GridItemCollection"/> class.
    /// </summary>
    /// <param name="owner">The owner.</param>
    /// <param name="component">The component.</param>
    public PropertyItemCollection(PropertyGrid owner, object component)
    {
      if (owner == null) throw new ArgumentNullException("owner");
      if (component == null) throw new ArgumentNullException("component");

      this.Owner = owner;
      this.Component = component;

      InitializeCollection(Owner, Component);
    }

    #endregion

    #region Initialization

    HashSet<string> cache = new HashSet<string>();

    /// <summary>
    /// Initializes the collection.
    /// </summary>
    /// <param name="owner">The owner.</param>
    /// <param name="component">The component.</param>
    protected virtual void InitializeCollection(PropertyGrid owner, object component)
    {
      cache = new HashSet<string>();

      // get property descriptors
      PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(component,
        new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues | 
                                                      PropertyFilterOptions.UnsetValues | 
                                                      PropertyFilterOptions.Valid) });


      List<PropertyItem> propertyCollection = new List<PropertyItem>();

      foreach (PropertyDescriptor propertyDescriptor in descriptors)
        CollectProperties(component, propertyDescriptor, propertyCollection);

      foreach (PropertyItem property in propertyCollection)
        InitializeProperty(property);

      cache.Clear();
    }

    /// <summary>
    /// Initializes the property.
    /// </summary>
    /// <param name="property">The property.</param>
    protected virtual void InitializeProperty(PropertyItem property)
    {      
      if (property == null) throw new ArgumentNullException("property");
      if (cache.Contains(property.PropertyName)) return;

      cache.Add(property.PropertyName);
      Items.Add(property);
    }

    /// <summary>
    /// Collects the properties.
    /// </summary>
    /// <param name="component">The component.</param>
    /// <param name="descriptor">The descriptor.</param>
    /// <param name="propertyList">The property list.</param>
    protected virtual void CollectProperties(object component, PropertyDescriptor descriptor, IList<PropertyItem> propertyList)
    {
      if (descriptor.Attributes[typeof(FlatternHierarchyAttribute)] == null)
      {
        PropertyItem item = Owner.CreateProperty(descriptor);
        if (item != null)
          propertyList.Add(item);
      }
      else
      {
        component = descriptor.GetValue(component);
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(component);
        foreach (PropertyDescriptor propertyDescriptor in properties)
          CollectProperties(component, propertyDescriptor, propertyList);
      }      
    }

    #endregion
  }
}
